//C- -------------------------------------------------------------------
//C- Java DjVu (r) (v. 0.8)
//C- Copyright (c) 2004-2005 LizardTech, Inc.  All Rights Reserved.
//C- Java DjVu is protected by U.S. Pat. No.C- 6,058,214 and patents
//C- pending.
//C-
//C- This software is subject to, and may be distributed under, the
//C- GNU General Public License, Version 2. The license should have
//C- accompanied the software or you may obtain a copy of the license
//C- from the Free Software Foundation at http://www.fsf.org .
//C-
//C- The computer code originally released by LizardTech under this
//C- license and unmodified by other parties is deemed "the LIZARDTECH
//C- ORIGINAL CODE."  Subject to any third party intellectual property
//C- claims, LizardTech grants recipient a worldwide, royalty-free,
//C- non-exclusive license to make, use, sell, or otherwise dispose of
//C- the LIZARDTECH ORIGINAL CODE or of programs derived from the
//C- LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU
//C- General Public License.   This grant only confers the right to
//C- infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to
//C- the extent such infringement is reasonably necessary to enable
//C- recipient to make, have made, practice, sell, or otherwise dispose
//C- of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to
//C- any greater extent that may be necessary to utilize further
//C- modifications or combinations.
//C-
//C- The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY
//C- OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
//C- TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF
//C- MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
//C-
//C- In addition, as a special exception, LizardTech Inc. gives permission
//C- to link the code of this program with the proprietary Java
//C- implementation provided by Sun (or other vendors as well), and
//C- distribute linked combinations including the two. You must obey the
//C- GNU General Public License in all respects for all of the code used
//C- other than the proprietary Java implementation. If you modify this
//C- file, you may extend this exception to your version of the file, but
//C- you are not obligated to do so. If you do not wish to do so, delete
//C- this exception statement from your version.
//C- -------------------------------------------------------------------
//C- Developed by Bill C. Riemers, Foxtrot Technologies Inc. as work for
//C- hire under US copyright laws.
//C- -------------------------------------------------------------------
//
package com.lizardtech.djvu{
import java.io.*;
import java.util.gc;


/**
 *  This class represents structured wavelette data.
 */
public class IWPixmap
  extends DjVuObject
  implements Codec
{
  //~ Static fields/initializers ---------------------------------------------
  
  public static const CRCBfull:int = 5;


  public static const CRCBhalf:int = 3;


  public static const CRCBMode:int = 1;


  public static const CRCBnone:int = 2;


  public static const CRCBnormal:int = 4;


  public static const rgb_to_ycc:Array =
  [
    new Array(0.304348, 0.608696, 0.086956),
    new Array(0.463768, -0.405797, -0.057971),
    new Array(-0.173913, -0.347826, 0.521739)
  ];

  //~ Instance fields --------------------------------------------------------


  protected var cbcodec:IWCodec;

  protected var crcodec:IWCodec;

  protected var ycodec:IWCodec;

  protected var cbmap:IWMap;

  protected var crmap:IWMap;

  protected var ymap:IWMap;

  protected var cbytes:int;

  protected var crcb_delay:int = 10;

  protected var crcb_half:Boolean= false;

  protected var cserial:int;

  protected var cslice:int;


  //~ Constructors -----------------------------------------------------------

  /**
   * Creates a new IWPixmap object.
   */
  public function IWPixmap():void {}

  //~ Methods ----------------------------------------------------------------

  /**
   * Query if this is image data.
   *
   * @return true
   */
  public function isImageData():Boolean{ 
      return true;
  }  
    
  /**
   * Creates an instance of IWPixmap with the options interherited from the
   * specified reference.
   *
   * @param ref Object to interherit DjVuOptions from.
   *
   * @return a new instance of IWPixmap.
   */
  public static function createIWPixmap(ref:DjVuInterface):IWPixmap{
    var options:DjVuOptions= ref.getDjVuOptions();

    return create(
      options,
      options.getIWPixmapClass(),
      "com.lizardtech.djvu.IWPixmap") as IWPixmap;
  }

  /**
   * DOCUMENT ME!
   */
  public function close_codec():void{
    ycodec   = crcodec = cbcodec = null;
    cslice   = cbytes = cserial = 0;

    if(DjVuOptions.COLLECT_GARBAGE)
    {
      //System.gc();
      java.util.gc.forceGC();
    }
  }

  /**
   * Decode this chunk.
   *
   * @param bs DOCUMENT ME!
   *
   * @throws IOException DOCUMENT ME!
   */
  //public function decode(bs:*, b:*=null):void // CachedInputStream
  public function decode(bs:InputStream, dict:JB2Dict=null):void // CachedInputStream
  {
    if(ycodec == null)
    {
      cslice   = cserial = 0;
      ymap     = null;
    }

    if(bs.read() != cserial)
    {
      throw new Error(
        "(IWPixmap::decode) Chunk does not bear expected serial number");
    }

    var nslices:int = cslice + bs.read();

    if(cserial == 0)
    {
      var major:int = bs.read();
      var minor:int = bs.read();

      if((major & 0x7) != 1)
      {
        throw new Error(
          "(IWPixmap::decode) File has been compressed with an incompatible IWCodec");
      }

      if(minor > 2)
      {
        throw new Error(
          "(IWPixmap::decode) File has been compressed with a more recent IWCodec");
      }

      var header3size:int = 5;

      if(minor < 2)
      {
        header3size = 4;
      }

      var w:int= (bs.read() << 8);
      w |= bs.read();

      var h:int= (bs.read() << 8);
      h |= bs.read();
      crcb_delay   = 0;
      crcb_half    = false;

      var b:int= bs.read();

      if(minor >= 2)
      {
        crcb_delay = 0x7 & b;
      }

      if(minor >= 2)
      {
        crcb_half = ((0x80 & b) == 0);
      }

      if((major & 0x80) != 0)
      {
        crcb_delay = -1;
      }

      ymap     = IWMap.createIWMap(this).init(w, h);
      ycodec   = IWCodec.createIWCodec(this).init(ymap);

      if(crcb_delay >= 0)
      {
        cbmap     = IWMap.createIWMap(this).init(w, h);
        crmap     = IWMap.createIWMap(this).init(w, h);
        cbcodec   = IWCodec.createIWCodec(this).init(cbmap);
        crcodec   = IWCodec.createIWCodec(this).init(crmap);
      }
    }

    var zp:ZPCodec= new ZPCodec(bs);

    for(var flag:int= 1; (flag != 0) && (cslice < nslices); cslice++)
    {
      flag = ycodec.code_slice(zp);

      if((crcodec != null) && (cbcodec != null) && (crcb_delay <= cslice))
      {
        flag |= cbcodec.code_slice(zp);
        flag |= crcodec.code_slice(zp);
      }
    }

    cserial++;

//    return nslices;
  }

  /**
   * DOCUMENT ME!
   *
   * @return DOCUMENT ME!
   */
  public function getHeight():int{
    return (ymap != null)
    ? ymap.ih
    : 0;
  }

  /**
   * DOCUMENT ME!
   *
   * @return DOCUMENT ME!
   */
  public function getPixmapA():GPixmap{
    if(ymap == null)
    {
      return null;
    }

    var w:int= ymap.iw;
    var h:int= ymap.ih;
    var pixsep:int= 3;
    var rowsep:int= w * pixsep;
    var bytes:Array= new Array(h * rowsep);

    ymap.image(0, bytes, rowsep, pixsep, false);

    if((crmap != null) && (cbmap != null) && (crcb_delay >= 0))
    {
      cbmap.image(1, bytes, rowsep, pixsep, crcb_half);
      crmap.image(2, bytes, rowsep, pixsep, crcb_half);
    }

    // Convert image to RGB
    var ppm:GPixmap=
      GPixmap.createGPixmap(this).init(bytes, h, w);
    var pixel:GPixelReference= ppm.createGPixelReference(0);

    for(var i:int= 0; i < h;)
    {
      pixel.setOffset(i++, 0);

      if((crmap != null) && (cbmap != null) && (crcb_delay >= 0))
      {
        pixel.YCC_to_RGB(w);
      }
      else
      {
        for(var x:int= w||0; x-- > 0; pixel.incOffset())
        {
          pixel.setGray(127- pixel.getBlue());
        }
      }
    }

    return ppm;
  }

  /**
   * Create a pixmap with the specified subsample rate and bounds.
   *
   * @param subsample rate at which to subsample
   * @param rect Bounding box of the desired pixmap.
   * @param retval An old pixmap to try updating, or null.
   *
   * @return DOCUMENT ME!
   */
  public function getPixmap(
    a:*=null,
    rect:GRect=null,
    retval:GPixmap=null):GPixmap{
    
    if(a == null) { // as3 overload
    	return getPixmapA() as GPixmap;
    }
    
    var subsample:int = int(a);	
    if(ymap == null)
    {
      return null;
    }

    if(retval == null)
    {
      retval = new GPixmap();
    }

    var w:int= rect.width();
    var h:int= rect.height();
    var pixsep:int= 3;
    var rowsep:int= w * pixsep;
    var bytes:Array= retval.init(h, w, null).data;

    ymap.image(subsample, rect.clone(), 0, bytes, rowsep, pixsep, false);

    if((crmap != null) && (cbmap != null) && (crcb_delay >= 0))
    {
      cbmap.image(subsample, rect.clone(), 1, bytes, rowsep, pixsep, crcb_half);
      crmap.image(subsample, rect.clone(), 2, bytes, rowsep, pixsep, crcb_half);
    }

    var pixel:GPixelReference= retval.createGPixelReference(0);

    for(var i:int= 0; i < h;)
    {
      pixel.setOffset(i++, 0);

      if((crmap != null) && (cbmap != null) && (crcb_delay >= 0))
      {
        pixel.YCC_to_RGB(w);
      }
      else
      {
        for(var x:int= w||0; x-- > 0; pixel.incOffset())
        {
          pixel.setGray(127- pixel.blueByte());
        }
      }
    }

    return retval;
  }

  /**
   * DOCUMENT ME!
   *
   * @return DOCUMENT ME!
   */
  public function getWidth():int{
    return (ymap != null)?ymap.iw:0;
  }

  /**
   * Set the CRCB Delay
   *
   * @param value the new CRCB delay value.
   *
   * @return the CRCB delay value
   */
  public function setCrcbDelay(value:int):int{
    if(value >= 0)
    {
      crcb_delay = value;
    }

    return crcb_delay;
  }
  
  
  public function decodeChunk(chunk:IFFChunk):void {
			if(ycodec == null)  {
		      cslice   = cserial = 0;
		      ymap     = null;
		    }
		
			var serial:int = chunk.read();
		    if(serial != cserial)
		    {
		      throw new Error(
		        "(IWPixmap::decode) Chunk does not bear expected serial number");
		    }
		
		    var nslices:int = cslice + chunk.read();
		
		    if(cserial == 0)
		    {
		      var major:int = chunk.read();
		      var minor:int = chunk.read();
		
		      if((major & 0x7) != 1)
		      {
		        throw new Error(
		          "(IWPixmap::decode) File has been compressed with an incompatible IWCodec");
		      }
		
		      if(minor > 2)
		      {
		        throw new Error(
		          "(IWPixmap::decode) File has been compressed with a more recent IWCodec");
		      }
		
		      var header3size:int = 5;
		
		      if(minor < 2)
		      {
		      	header3size = 4;
		      	chunk.position--;
		      }
		
			  trace("ser: " + serial + "."+major+"."+minor+ " " + chunk.position);	  
			  
			  var w:uint = chunk.readUInt16();
			  var h:uint = chunk.readUInt16();
		      
		      crcb_delay   = 0;
		      crcb_half    = false;
		
		      var b:int= chunk.read();
		
		      if(minor >= 2)
		      {
		        crcb_delay = 0x7f & b;
		      }
		
		      if(minor >= 2)
		      {
		        crcb_half = ((0x80 & b) == 0);
		      }
		
		      if((major & 0x80) != 0)
		      {
		        crcb_delay = -1;
		      }
		
		      ymap     = new IWMap(w, h);
		      ycodec   = new IWCodec().init(ymap);
		
		      if(crcb_delay >= 0)
		      {
		        cbmap     = new IWMap(w, h);
		        crmap     = new IWMap(w, h);
		        cbcodec   = new IWCodec().init(cbmap);
		        crcodec   = new IWCodec().init(crmap);
		      }
		    }

			chunk.position += 1;
		
		    var zp:ZPCodec = new ZPCodec(chunk);
		
		    for(var flag:int= 1; (flag != 0) && (cslice < nslices); cslice++)
		    {
		      trace("Decoding slice: " + cslice + "/" + nslices);
		      flag = ycodec.code_slice(zp);
		
		      if((crcodec != null) && (cbcodec != null) && (crcb_delay <= cslice))
		      {
		        flag |= cbcodec.code_slice(zp);
		        flag |= crcodec.code_slice(zp);
		      }
		    }
		
		    cserial++;
		    trace("decoded BG44: " + nslices + " slices.");
		}
}}